#include "String.h"

/** 
 * \todo:
 *	Finish making the Find* Functions
 */

inline int StrCheckRealloc(S_String *Str, int Size)
{
	/* This function checks if a string needs to allocate more bytes
	 *	to handle the incoming data, with a size of Size
	*/
	printf("%i\n", Size);
	if(Str->Size < Size)
	{
		/* The string does not have enough memory allocated,
		 *	let's ask for more
		*/
		void *TempPointer = realloc(Str->String, Size);
		if(TempPointer == NULL)
			return -1;
		Str->String = TempPointer;
		Str->Size = Size;
	}
	/* Else the string does not need to be changed, and the 
	 *	pointer and size variables can stay the same
	*/
	return 0;
}

S_String *S_NewString(char *String)
{
	/* Allocate a new S_String object on the heap,
		check the allocation, and return the object (or NULL)
	*/
	S_String *Ret = malloc(sizeof(Ret));
	if(Ret == NULL)
		return NULL;
	Ret->String = malloc(sizeof(Ret->String)*strlen(String));
	Ret->Size = strlen(String);
	if(Ret->String == NULL)
		return NULL;
	if(S_SetString(Ret, String) == -1)
		return NULL;
	Ret->Length = strlen(Ret->String);
	return Ret;
}
S_String *S_NewStringSafe(char *String, int Length)
{
	S_String *Ret = malloc(sizeof(Ret));
	if(Ret == NULL)
		return NULL;
	Ret->String = malloc(sizeof(Ret->String)*Length);
	if(Ret->String == NULL)
		return NULL;
	if(S_SetStringSafe(Ret, String, Length) == -1)
		return NULL;
	Ret->Length = Length;
	Ret->Size = Length;
	return Ret;
}
S_String *S_NewStringS(S_String *String)
{
	return S_NewStringSafe(S_GetCString(String), S_GetStringLength(String));
}
void S_FreeString(S_String *String)
{
	/* Free all of the data that is being used by the S_String object */
	free(String->String);
	free(String);
}

int S_SetString(S_String *String, char *Str)
{
	int /*Count, */Size = strlen(Str);
	/*void *TempPointer = realloc(String->String, sizeof(String->String)*Size);
	if(TempPointer == NULL)
		return -1;
	String->String = TempPointer;
	*/
	if(StrCheckRealloc(String, Size) == -1)
		return -1;
	/*for(Count = 0; Count < Size; ++Count)
	{
		String->String[Count] = Str[Count];
	}*/
	/* Copy the string over, by now the string is guarenteed to have enough memory */
	memcpy(String->String, Str, Size);
	String->Length = Size;
	return 0;
}
int S_SetStringS(S_String *String, S_String *SetString)
{
	/*char *TempPointer = realloc(String->String, sizeof(String->String)*SetString->Length);
	if(TempPointer == NULL)
		return -1;
	String->String = TempPointer;*/
	if(StrCheckRealloc(String, SetString->Length) == -1)
		return -1;
	memcpy(String->String, SetString->String, SetString->Length);
	String->Length = SetString->Length;
	return 0;
}
int S_SetStringSafe(S_String *String, char *Str, int Length)
{
	/*int Count;*/
	/*char *TempPointer = realloc(String->String, sizeof(String->String)*Length);
	if(TempPointer == NULL)
		return -1;
	String->String = TempPointer;*/
	if(StrCheckRealloc(String, Length) == -1)
		return -1;
	/*for(Count = 0; Count < Length; ++Count)
	{
		String->String[Count] = Str[Count];
	}*/
	memcpy(String->String, Str, Length);
	String->Length = Length;
	return 0;
}

void S_PrintSafe(S_String *String)
{
	/* Print the string, char by char, so that all of the string will be preserved,
		rather than just printf running into a null byte then stopping
	*/
	int Count;
	for(Count = 0; Count < String->Length; ++Count)
	{
		printf("%c", String->String[Count]);
	}
}

int S_AppendString(S_String *String, char *Str)
{
	int Length = strlen(Str);
	/*char *TempPointer = realloc(String->String, sizeof(String->String)*(String->Length+Length));
	if(TempPointer == NULL)
		return -1;
	String->String = TempPointer;*/
	if(StrCheckRealloc(String, String->Length+Length) == -1)
		return -1;
	/* Increase the string pointer by the length of the string, and then append the new string to that pointer */
	memcpy(String->String + String->Length-1, Str, Length);
	String->Length += Length;
	return 0;
}

int S_AppendStringS(S_String *Str, S_String *AppendString)
{
	/*char *TempPointer = realloc(Str->String, sizeof(Str->String)*(Str->Length+AppendString->Length));
	if(TempPointer == NULL)
		return -1;
	Str->String = TempPointer;*/
	if(StrCheckRealloc(Str, (Str->Length+AppendString->Length)) == -1)
		return -1;
	memcpy(Str->String + Str->Length, AppendString->String, AppendString->Length);
	return 0;
}
int S_AppendStringSafe(S_String *String, char *Str, int Length)
{
	/*char *TempPointer = realloc(String->String, sizeof(String->String)*(String->Length+Length));
	if(TempPointer == NULL)
		return -1;
	String->String = TempPointer;*/
	if(StrCheckRealloc(String, (String->Length+Length)) == -1)
		return -1;
	memcpy(String->String + String->Length, Str, Length);
	return 0; 
}

int S_AppendChar(S_String *String, char C)
{
	/* Quick... */
	char Temp[2] = {C, '\0'};
	return S_AppendString(String, Temp);
}

int S_InsertString(S_String *String, int Offset, char *Str)
{
	/*FIXME:
		This function could be changed to not use any Temp arrays,
		and just relying on the optomisations of the C libraries
		memmove and memcpy
	*/
	if(Offset > 0)
	{
		/* Make enough space for the original string, then copy over the original string in two sections */
		char Temp1[Offset];
		memcpy(Temp1, String->String, Offset);
		char Temp2[String->Length-Offset];
		memcpy(Temp2, String->String+Offset, String->Length - Offset+1);
		/*void *TempPointer = realloc(String->String, sizeof(String->String)*String->Length+strlen(Str));
		if(TempPointer == NULL)
			return -1;
		String->String = TempPointer;*/
		if(StrCheckRealloc(String, (String->Length+strlen(Str))) == -1)
			return -1;
		/* Put all the strings back, in the (new) correct order */
		memcpy(String->String, Temp1, Offset);
		memcpy(String->String + Offset, Str, strlen(Str));
		memcpy(String->String + Offset + strlen(Str), Temp2, String->Length-Offset);
		String->Length += strlen(Str);
		return 0;
	}	
	else
	{
		return S_PrependString(String, Str);
	}
}
int S_InsertStringS(S_String *String, int Offset, S_String *InsertString)
{
	if(Offset > 0)
	{
		char Temp1[Offset];
		memcpy(Temp1, String->String, Offset);
		char Temp2[String->Length-Offset];
		memcpy(Temp2, String->String+Offset, String->Length - Offset+1);
		/*void *TempPointer = realloc(String->String, sizeof(String->String)*String->Length+InsertString->Length);
		if(TempPointer == NULL)
			return -1;
		String->String = TempPointer;*/
		if(StrCheckRealloc(String, (String->Length+InsertString->Length)) == -1)
			return -1;
		memcpy(String->String, Temp1, Offset);
		memcpy(String->String + Offset, InsertString->String, InsertString->Length);
		memcpy(String->String + Offset + InsertString->Length, Temp2, String->Length-Offset);
		String->Length += InsertString->Length;
		return 0;
	}
	else
	{
		return S_PrependStringS(String, InsertString);
	}
}

int S_InsertStringSafe(S_String *String, int Offset, char *Str, int Length)
{
	if(Offset > 0)
	{
		char Temp1[Offset];
		char Temp2[String->Length-Offset];
		memcpy(Temp1, String->String, Offset);
		memcpy(Temp2, String->String+Offset, String->Length - Offset+1);
		if(StrCheckRealloc(String, (String->Length+Length)) == -1)
			return -1;
		memcpy(String->String, Temp1, Offset);
		memcpy(String->String + Offset, Str, Length);
		memcpy(String->String + Offset + Length, Temp2, String->Length - Offset);
		String->Length += Length;
		return 0;
	}
	else
	{
		return S_PrependStringSafe(String, Str, Length);
	}
}

int S_PrependString(S_String *String, char *Str)
{
	/* FIXME:
		The prepend functions are quite slow, due to the
		shifting of the whole string by an arbritary amount.
		Optomisation ideas welcome
	*/
	int Size =strlen(Str);
	if(StrCheckRealloc(String, Size+String->Length) == -1)
		return -1;
	/* Move the string that is already present to an offset of the length of the prepended string*/
	memmove(String->String + Size, String->String, String->Length);
	/* Copy the prepended string into the start of the original string */
	memcpy(String->String, Str, Size);
	String->Length += Size;
	return 0;
}
int S_PrependStringS(S_String *String, S_String *PString)
{
	if(StrCheckRealloc(String, PString->Length+String->Length) == -1)
		return -1;
	memmove(String->String+PString->Length, String->String, String->Length);
	memcpy(String->String, PString->String, PString->Length);
	String->Length += PString->Length;
	return 0;
}
int S_PrependStringSafe(S_String *String, char *Str, int Length)
{
	if(StrCheckRealloc(String, Length+String->Length) == -1)
		return -1;
	memmove(String->String+Length, String->String, String->Length);
	memcpy(String->String, Str, Length);
	String->Length += Length;
	return 0;
}

int S_SetSize(S_String *String, int Size)
{
	/* Set the capacity of the string, note that this is not the same as the length of the string */
	if(String->Size == Size)
		return 0;
	void *TempPointer = realloc(String->String, sizeof(String->String)*Size);
	if(TempPointer == NULL)
		return -1;
	String->String = TempPointer;
	String->Size = Size;
	return 0;
}

/*
char S_At(S_String *String, int At)
{
	return String->String[At];
} This function is now a macro */

void S_StringReverse(S_String *String)
{
	char *Front, *Back;
	for(Front = String->String, Back = String->String + String->Length -1;
		Front < Back;
		++Front, --Back)
	{	
		char Tmp;
		Tmp = *Front;
		*Front = *Back;
		*Back = Tmp;
	}
}

int S_FindFirstC(S_String *String, char Search)
{
	/* Go from the start comparing bytes */
	int Count = 0;
	while(Count < String->Length)
	{
		if(String->String[Count] == Search)
			return Count;
		++Count;
	}
	/* No identical byte was found */
	return -1;
}
int S_FindLastC(S_String *String, char Search)
{
	/* Start at the end comparing bytes */
	int Count = String->Length - 1;
	while(Count >= 0)
	{
		if(String->String[Count] == Search)
			return Count;
		--Count;
	}
	return -1;
}
int S_FindFirst(S_String *String, char *Search)
{
	int Count = 0, Size = strlen(Search);
	while(Count+Size < String->Length)
	{
		if(memcmp(String->String+Count, Search, Size) == 0)
			return Count;
		++Count;
	}
	return -1;
}
int S_FindLast(S_String *String, char *Search);
int S_FindFirstS(S_String *String, S_String *Search);
int S_FindLastS(S_String *String, S_String *Search);
int S_FindFirstSafe(S_String *String, char *Search, int Length);
int S_FindLastSafe(S_String *String, char *Search, int Length);

void S_ToUpper(S_String *String)
{
	int Count;
	for(Count = 0; Count < String->Length; ++Count)
		if(islower(String->String[Count]))String->String[Count] = toupper(String->String[Count]);
}

void S_Strip(S_String *String)
{
	/* Remove all of the whitespace from the start and end of the string */
	int ResultingLength = String->Length, Index, StartOffset = 0;
	for(Index = 0; Index < String->Length; ++Index)
	{
		if(String->String[Index] == 10 || String->String[Index] == 13 ||
		String->String[Index] == 32 || String->String[Index] == 9)
		{
			++StartOffset;
			--ResultingLength;
		}
		else
			break;
	}
	for(Index = String->Length - 1; Index >= 0; --Index)
	{
		if(String->String[Index] == 10 || String->String[Index] == 13 ||
		String->String[Index] == 32 || String->String[Index] == 9)
		{
			--ResultingLength;
		}
		else
			break;
	}
	memmove(String->String, String->String+StartOffset, ResultingLength);
	String->Length = ResultingLength;
}

int S_PadFront(S_String *Str, int Length, char PadChar)
{
	if(Str->Length >= Length) /* The string does not need to be padded... */
		return 0;
	if(StrCheckRealloc(Str, Length) == -1)
		return -1;
	char Temp[Length-Str->Length];
	/* Was using memset here, until IllegalInstruction exceptions were being thrown. FML */
	int Index = 0;
	while(Index < Length - Str->Length)
		Temp[Index++] = PadChar;
	return S_PrependStringSafe(Str, Temp, Length-Str->Length);
}

S_String *S_SubString(S_String *String, int Offset, int Length)
{
	return S_NewStringSafe(String->String+Offset, Length);
}

char *S_GetCString(S_String *Str)
{
	if(S_GetSize(Str) == S_GetStringLength(Str))
		S_SetSize(Str, S_GetStringLength(Str)+1);
	Str->String[S_GetStringLength(Str)] = '\0';
/*	int A;
	printf("---------------------------------------- Length: %i\n", Str->Length);
	for(A = 0; A < Str->Size; ++A)
	{
		printf("%i\n", S_At(Str, A));
	}
	printf("----------------------------------------\n");*/
	return Str->String;
}

D_Array S_Explode(S_String *String, char C)
{
	D_Array Ret;
	Ret = NewArray(1);
	int Index = 0;
	
	return Ret;
}
